import React, { useState, useEffect, useRef } from 'react';
import style from './Liao.module.css';
import { useNavigate } from 'react-router-dom';
import TransferCard from './TransferCard';
import CameraModal from './CameraModal';
import { LineChart, Line, XAxis, YAxis, Tooltip, ResponsiveContainer, CartesianGrid } from 'recharts';

// 新增：所有表情分组及图片
const EMOJI_GROUPS = [
  {
    key: '收藏',
    name: '收藏',
    images: ['/emoji/收藏/1.jpg', '/emoji/收藏/2.jpg', '/emoji/收藏/3.jpg'],
  },
  {
    key: 'lan',
    name: '懒洋洋',
    images: ['/emoji/lan/1.gif', '/emoji/lan/2.gif', '/emoji/lan/3.gif', '/emoji/lan/4.gif', '/emoji/lan/5.gif', '/emoji/lan/6.gif', '/emoji/lan/7.gif'],
  },
  {
    key: 'zhuzhuxia',
    name: '猪猪侠',
    images: ['/emoji/zhuzhuxia/1.jpg', '/emoji/zhuzhuxia/2.jpg', '/emoji/zhuzhuxia/3.jpg'],
  },
  {
    key: 'naichashu',
    name: '奶茶鼠',
    images: ['/emoji/naichashu/1.webp', '/emoji/naichashu/2.webp', '/emoji/naichashu/3.webp', '/emoji/naichashu/4.webp', '/emoji/naichashu/5.webp', '/emoji/naichashu/6.webp', '/emoji/naichashu/7.webp', '/emoji/naichashu/8.webp', '/emoji/naichashu/9.webp', '/emoji/naichashu/10.webp', '/emoji/naichashu/11.webp', '/emoji/naichashu/12.webp'],
  },
  {
    key: 'zhang',
    name: '章鱼哥',
    images: ['/emoji/zhang/1jpg.jpg', '/emoji/zhang/2.jpg', '/emoji/zhang/3.jpg', '/emoji/zhang/4.jpg', '/emoji/zhang/5.jpg', '/emoji/zhang/6.jpg', '/emoji/zhang/7.jpg', '/emoji/zhang/8.jpg', '/emoji/zhang/9.jpg', '/emoji/zhang/10.jpg', '/emoji/zhang/11.jpg', '/emoji/zhang/12.jpg', '/emoji/zhang/13.jpg', '/emoji/zhang/14.jpg', '/emoji/zhang/15.jpg', '/emoji/zhang/16.jpg', '/emoji/zhang/17.jpg', '/emoji/zhang/18.jpg'],
  },
];

// 小火人SVG动画组件
function FireMan({ type = 'default', size = 38 }) {
  if (type === 'blue') {
    return (
      <svg width={size} height={size} viewBox="0 0 48 48" className={style.firemanSvg}><ellipse cx="24" cy="28" rx="16" ry="18" fill="#4fc3f7" /><ellipse cx="19" cy="30" rx="2" ry="2.2" fill="#fff" /><ellipse cx="29" cy="30" rx="2" ry="2.2" fill="#fff" /><ellipse cx="19" cy="30" rx="1" ry="1.1" fill="#333" /><ellipse cx="29" cy="30" rx="1" ry="1.1" fill="#333" /><ellipse cx="24" cy="34" rx="2.5" ry="1.2" fill="#b3e5fc" /><path d="M21 33 Q24 36 27 33" stroke="#0288d1" strokeWidth="1.2" fill="none"/><ellipse cx="13" cy="40" rx="3" ry="2" fill="#b3e5fc"/><ellipse cx="35" cy="40" rx="3" ry="2" fill="#b3e5fc"/></svg>
    );
  }
  if (type === 'red') {
    return (
      <svg width={size} height={size} viewBox="0 0 48 48" className={style.firemanSvg}><ellipse cx="24" cy="28" rx="16" ry="18" fill="#ff5252" /><ellipse cx="19" cy="30" rx="2" ry="2.2" fill="#fff" /><ellipse cx="29" cy="30" rx="2" ry="2.2" fill="#fff" /><ellipse cx="19" cy="30" rx="1" ry="1.1" fill="#333" /><ellipse cx="29" cy="30" rx="1" ry="1.1" fill="#333" /><ellipse cx="24" cy="34" rx="2.5" ry="1.2" fill="#ffb380" /><path d="M21 33 Q24 36 27 33" stroke="#e67c00" strokeWidth="1.2" fill="none"/><ellipse cx="13" cy="40" rx="3" ry="2" fill="#ffb380"/><ellipse cx="35" cy="40" rx="3" ry="2" fill="#ffb380"/></svg>
    );
  }
  // 默认形象
  return (
    <svg width={size} height={size} viewBox="0 0 48 48" className={style.firemanSvg}><ellipse cx="24" cy="28" rx="16" ry="18" fill="url(#firemanBody)" /><defs><radialGradient id="firemanBody" cx="50%" cy="60%" r="60%"><stop offset="0%" stopColor="#fffde4"/><stop offset="80%" stopColor="#ffe259"/><stop offset="100%" stopColor="#ff6a00"/></radialGradient></defs><ellipse cx="19" cy="30" rx="2" ry="2.2" fill="#fff" /><ellipse cx="29" cy="30" rx="2" ry="2.2" fill="#fff" /><ellipse cx="19" cy="30" rx="1" ry="1.1" fill="#333" /><ellipse cx="29" cy="30" rx="1" ry="1.1" fill="#333" /><ellipse cx="24" cy="34" rx="2.5" ry="1.2" fill="#ffb380" /><path d="M21 33 Q24 36 27 33" stroke="#e67c00" strokeWidth="1.2" fill="none"/><ellipse cx="13" cy="40" rx="3" ry="2" fill="#ffb380"/><ellipse cx="35" cy="40" rx="3" ry="2" fill="#ffb380"/></svg>
  );
}

// 小火人形象列表
const FIREMAN_LIST = [
  { key: 'default', name: '默认', icon: '/fireman/default.svg', desc: '阳光活力，软萌可亲' },
  { key: 'blue', name: '蓝色火人', icon: '/fireman/blue.svg', desc: '冷静理智，呆萌小蓝' },
  { key: 'red', name: '红色火人', icon: '/fireman/red.svg', desc: '热情如火，元气小红' },
];

export default function Liao() {
    const navigate= useNavigate();
    const targetUserName = '爱吃柠檬';
    // 初始化时从 localStorage 加载
    const [messages, setMessages] = useState(() => {
        try {
            const saved = localStorage.getItem('liao_chat_messages');
            return saved ? JSON.parse(saved) : [];
        } catch {
            return [];
        }
    });
    // 自动保存到 localStorage
    useEffect(() => {
        try {
            localStorage.setItem('liao_chat_messages', JSON.stringify(messages));
        } catch {}
    }, [messages]);
    const [inputMessage, setInputMessage] = useState('');
    const [isConnected, setIsConnected] = useState(false);
    const [ws, setWs] = useState(null);
    const [connectionError, setConnectionError] = useState('');
    const messagesEndRef = useRef(null);
    const [userId] = useState('user_001');
    const [targetUserId, setTargetUserId] = useState('user_002');
    const [isVoiceMode, setIsVoiceMode] = useState(false);
    const [showPlusPanel, setShowPlusPanel] = useState(false);
    const [isRecording, setIsRecording] = useState(false);
    const [recordTime, setRecordTime] = useState(0);
    const recordTimerRef = useRef(null);
    const recordTimeRef = useRef(0);
    // 语音已读状态
    const [voiceReadMap, setVoiceReadMap] = useState(() => {
        // 从 localStorage 读取已读状态
        const saved = localStorage.getItem('voiceReadMap');
        const parsed = saved ? JSON.parse(saved) : {};
        return parsed;
    }); // {msgId: true/false}
    // 录音音量动画
    const [volumeLevel, setVolumeLevel] = useState(1);
    const volumeTimerRef = useRef(null);
    // 录音相关
    const [mediaRecorder, setMediaRecorder] = useState(null);
    const [audioChunks, setAudioChunks] = useState([]);
    const [audioUrl, setAudioUrl] = useState('');
    const [playingAudioId, setPlayingAudioId] = useState(null);
    const [recordError, setRecordError] = useState('');
    const [audioMimeType, setAudioMimeType] = useState('audio/webm');
    const analyserRef = useRef(null);
    const animationRef = useRef(null);
    const [selectedFiles, setSelectedFiles] = useState([]);
    const fileInputRef = useRef(null);
    const [showImagePicker, setShowImagePicker] = useState(false);
    const publicImages = [
      '/10.jpg', '/11.jpg', '/12.jpg', '/13.jpg', '/14.jpg', '/19.png', '/20.png',
      '/微信头像.jpg', '/个人头像.png', '/净化器.png', '/卡比龙总裁烟.png', '/咖啡机.png',
      '/手表.png', '/拉菲.jfif', '/无线充电器.png', '/无线耳机.png', '/洋酒.png', '/电脑.png',
      '/白酒.png', '/礼盒.png', '/红酒.png', '/配件.png', '/香烟.png','/bg-user.jpg','/bg-user2.jpg','/bg-user3.jpg','/bg-user4.jpg'
    ];

    const [selectedPublicImages, setSelectedPublicImages] = useState([]);
    // 1. 预览相关状态
    const [previewImages, setPreviewImages] = useState([]); // 所有图片url
    const [previewIndex, setPreviewIndex] = useState(0);    // 当前预览索引
    const [previewTip, setPreviewTip] = useState('');
    const [showCallSheet, setShowCallSheet] = useState(false);
    const [showMoreMenu, setShowMoreMenu] = useState(false);

    // 选择模式相关状态
    const [isSelectMode, setIsSelectMode] = useState(false);
    const [selectedMessages, setSelectedMessages] = useState([]);

    const [showChatHistory, setShowChatHistory] = useState(false);

    // 关闭更多菜单的全局点击事件
    useEffect(() => {
      if (!showMoreMenu) return;
      const close = () => setShowMoreMenu(false);
      window.addEventListener('click', close);
      return () => window.removeEventListener('click', close);
    }, [showMoreMenu]);

    // 优化 scrollToBottom，支持平滑和直接跳转
    const scrollToBottom = (smooth = false) => {
      messagesEndRef.current?.scrollIntoView({ behavior: smooth ? "smooth" : "auto" });
    };
    const firstLoadRef = useRef(true);
    useEffect(() => {
      if (firstLoadRef.current) {
        scrollToBottom(false); // 直接跳到底部
        firstLoadRef.current = false;
      } else {
        scrollToBottom(true); // 平滑滚动
      }
    }, [messages]);

    // WebSocket 连接
    useEffect(() => {
        // 这里使用一个模拟的WebSocket服务器地址，你需要替换为实际的服务器地址
        const wsUrl = 'ws://localhost:8080';

        const websocket = new WebSocket(wsUrl);

        websocket.onopen = () => {
            console.log('WebSocket连接已建立');
            setIsConnected(true);
            setConnectionError('');

            // 发送用户身份信息
            const authMessage = {
                type: 'auth',
                userId: userId,
                action: 'connect'
            };
            websocket.send(JSON.stringify(authMessage));
        };

        websocket.onmessage = (event) => {
            try {
                const data = JSON.parse(event.data);

                switch (data.type) {
                    case 'message':
                        setMessages(prev => [...prev, {
                            id: Date.now(),
                            text: data.message,
                            sender: data.senderId,
                            timestamp: new Date().toISOString(),
                            isOwn: data.senderId === userId
                        }]);
                        break;
                    case 'system':
                        setMessages(prev => [...prev, {
                            id: Date.now(),
                            text: data.message,
                            sender: 'system',
                            timestamp: new Date().toISOString(),
                            isOwn: false
                        }]);
                        break;
                    case 'auth':
                        if (data.status === 'success') {
                            // 默认发送消息
                            // 这个我可以用在 添加好友后 首次发送的消息

                            // setMessages(prev => [...prev, {
                            //     id: Date.now(),
                            //     text: '干啥呢',
                            //     sender: 'system',
                            //     timestamp: new Date().toISOString(),
                            //     isOwn: false
                            // }]);
                        }
                        break;
                    case 'voice':
                        // 处理语音消息
                        setMessages(prev => [...prev, {
                            id: data.msgId || Date.now(),
                            type: 'voice',
                            sender: data.senderId,
                            isOwn: data.senderId === userId,
                            duration: data.duration,
                            audioBase64: data.audioBase64,
                            timestamp: new Date().toISOString()
                        }]);
                        break;
                    case 'transfer':
                        setMessages(prev => [...prev, {
                            id: data.msgId || Date.now(),
                            type: 'transfer',
                            sender: data.senderId,
                            isOwn: data.senderId === userId,
                            amount: data.amount,
                            remark: data.remark,
                            senderName: data.senderName,
                            receiverName: data.receiverName,
                            timestamp: new Date().toISOString()
                        }]);
                        break;
                    case 'image':
                        setMessages(prev => [...prev, {
                            id: data.msgId || Date.now(),
                            type: 'image',
                            sender: data.senderId,
                            isOwn: data.senderId === userId,
                            fileUrl: data.fileUrl,
                            timestamp: new Date().toISOString()
                        }]);
                        break;
                    case 'emoji':
                        setMessages(prev => [...prev, {
                            id: data.msgId || Date.now(),
                            type: 'emoji',
                            sender: data.senderId,
                            isOwn: data.senderId === userId,
                            text: data.text,
                            timestamp: new Date().toISOString()
                        }]);
                        break;
                    case 'location':
                        setMessages(prev => [...prev, {
                            id: data.msgId || Date.now(),
                            type: 'location',
                            sender: data.senderId,
                            isOwn: data.senderId === userId,
                            timestamp: new Date().toISOString()
                        }]);
                        break;
                    default:
                        console.log('收到未知消息类型:', data);
                }
            } catch (error) {
                console.error('解析消息失败:', error);
            }
        };

        websocket.onclose = (event) => {
            console.log('WebSocket连接已关闭:', event.code, event.reason);
            setIsConnected(false);
            setConnectionError('连接已断开');
        };

        websocket.onerror = (error) => {

            setIsConnected(false);
            setConnectionError('连接失败，请检查服务器是否运行');
        };

        setWs(websocket);

        // 清理函数
        return () => {
            websocket.close();
        };
    }, [userId]);

    // 发送消息
    const sendMessage = () => {
        if (!inputMessage.trim()) {
            alert('请输入消息内容');
            return;
        }

        if (!isConnected) {
            alert('WebSocket未连接，请检查服务器是否运行');
            return;
        }

        if (!ws || ws.readyState !== WebSocket.OPEN) {
            alert('网络连接异常，请刷新页面重试');
            return;
        }

        const messageData = {
            type: 'message',
            message: inputMessage,
            senderId: userId,
            receiverId: targetUserId,
            timestamp: new Date().toISOString()
        };

        try {
            ws.send(JSON.stringify(messageData));

            // 添加到本地消息列表
            setMessages(prev => [...prev, {
                id: Date.now(),
                text: inputMessage,
                sender: userId,
                timestamp: new Date().toISOString(),
                isOwn: true
            }]);

            setInputMessage('');
            
            // 消息发送完成后自动滚动到底部
            setTimeout(() => {
                scrollToBottom();
            }, 100);
        } catch (error) {
            console.error('发送消息失败:', error);
            alert('发送消息失败: ' + error.message);
        }
    };

    // 处理回车键发送
    const handleKeyPress = (e) => {
        if (e.key === 'Enter' && !e.shiftKey) {
            e.preventDefault();
            sendMessage();
        }
    };

    // 模拟接收消息（用于测试）
    const simulateReceiveMessage = () => {
        const mockMessage = {
            id: Date.now(),
            text: `这是来自 ${targetUserId || '其他用户'} 的模拟消息`,
            sender: targetUserId || 'other_user',
            timestamp: new Date().toISOString(),
            isOwn: false
        };
        setMessages(prev => [...prev, mockMessage]);
    };

    // 重新连接
    const reconnect = () => {
        if (ws) {
            ws.close();
        }
        setConnectionError('');
        // 重新连接会在useEffect中自动触发
    };

    // 播放语音气泡时详细提示
    const handleVoiceClick = (msg) => {
        if (msg.audioBase64) {
            // 立即设置为已读（点击时立即消失红点）
            if (!msg.isOwn && !voiceReadMap[msg.id]) {
                setVoiceReadMap(v => {
                    const newMap = { ...v, [msg.id]: true };
                    // 保存到 localStorage
                    localStorage.setItem('voiceReadMap', JSON.stringify(newMap));
                    return newMap;
                });
            }

            const mime = msg.mimeType || 'audio/webm';
            const audio = new window.Audio('data:' + mime + ';base64,' + msg.audioBase64);
            setPlayingAudioId(msg.id);
            audio.play().catch(e => {
                alert('音频播放失败: ' + e.message + '\n格式:' + mime + '\n请用最新版Chrome/Edge测试，或在控制台window.lastMessages查看base64。');
                console.error('音频播放失败', e, msg);
            });
            audio.onended = () => {
                setPlayingAudioId(null);
            };
        } else {
            alert('没有可播放的语音数据，请检查录音权限或浏览器兼容性。');
        }
    };

    // 录音时长计时
    // useEffect(() => {
    //     let timer = null;
    //     if (isRecording) {
    //         setRecordTime(0);
    //         timer = setInterval(() => {
    //             setRecordTime(rt => rt + 1);
    //         }, 1000);
    //     } else if (!isRecording && recordTime !== 0) {
    //         setRecordTime(0);
    //     }
    //     return () => timer && clearInterval(timer);
    // }, [isRecording]);

    // 空格键录音快捷键
    useEffect(() => {
        const handleKeyDown = (e) => {
            const active = document.activeElement;
            if ((e.code === 'Space' || e.keyCode === 32) && !isRecording && isVoiceMode && active.tagName !== 'INPUT' && active.tagName !== 'TEXTAREA') {
                e.preventDefault();
                handleVoiceBtnClick();
            }
        };
        const handleKeyUp = (e) => {
            if ((e.code === 'Space' || e.keyCode === 32) && isRecording) {
                e.preventDefault();
                handleVoiceBtnClick();
            }
        };
        window.addEventListener('keydown', handleKeyDown);
        window.addEventListener('keyup', handleKeyUp);
        return () => {
            window.removeEventListener('keydown', handleKeyDown);
            window.removeEventListener('keyup', handleKeyUp);
        };
    }, [isRecording, isVoiceMode]);

    // 便于调试
    useEffect(() => {
        window.lastMessages = messages;
    }, [messages]);

    const handleVoiceBtnClick = async () => {
        if (!isRecording) {
            // 开始录音
            if (navigator.mediaDevices && window.MediaRecorder) {
                const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
                let localChunks = [];
                const mr = new window.MediaRecorder(stream);
                mr.ondataavailable = e => localChunks.push(e.data);
                mr.onstop = () => {
                    // 1. 先清除定时器，确保recordTimeRef.current为最终时长
                    if (recordTimerRef.current) clearInterval(recordTimerRef.current);
                    setRecordTime(recordTimeRef.current);
                    const blob = new Blob(localChunks, { type: 'audio/webm' });
                    const url = URL.createObjectURL(blob);
                    setAudioUrl(url);

                    // 普通语音消息模式
                    const reader = new FileReader();
                    reader.onloadend = () => {
                        const base64data = reader.result.split(',')[1];
                        const duration = recordTimeRef.current || 1;
                        const voiceMsg = {
                            type: 'voice',
                            senderId: userId,
                            receiverId: targetUserId,
                            duration,
                            audioBase64: base64data,
                            msgId: Date.now(),
                            timestamp: new Date().toISOString()
                        };
                        ws && ws.readyState === 1 && ws.send(JSON.stringify(voiceMsg));
                        // 本地也显示气泡
                        setMessages(prev => [...prev, {
                            id: voiceMsg.msgId,
                            type: 'voice',
                            sender: userId,
                            isOwn: true,
                            duration: voiceMsg.duration,
                            audioBase64: base64data,
                            timestamp: new Date().toISOString()
                        }]);
                        
                        // 语音消息发送完成后自动滚动到底部
                        setTimeout(() => {
                            scrollToBottom();
                        }, 100);
                    };
                    reader.readAsDataURL(blob);
                };
                mr.start();
                setMediaRecorder(mr);
                setIsRecording(true);

                // 音量分析
                const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
                const source = audioCtx.createMediaStreamSource(stream);
                const analyser = audioCtx.createAnalyser();
                analyser.fftSize = 256;
                source.connect(analyser);
                analyserRef.current = analyser;

                const dataArray = new Uint8Array(analyser.frequencyBinCount);
                const updateVolume = () => {
                    analyser.getByteTimeDomainData(dataArray);
                    const max = Math.max(...dataArray);
                    setVolumeLevel(Math.floor((max - 128) / 8) + 1);
                    animationRef.current = requestAnimationFrame(updateVolume);
                };
                updateVolume();

                // 录音时长定时器
                setRecordTime(0);
                recordTimeRef.current = 0;
                if (recordTimerRef.current) clearInterval(recordTimerRef.current);
                recordTimerRef.current = setInterval(() => {
                    setRecordTime(t => {
                        recordTimeRef.current = t + 1;
                        return t + 1;
                    });
                }, 1000);
            } else {
                alert('当前浏览器不支持录音');
            }
        } else {
            // 结束录音
            if (mediaRecorder) {
                mediaRecorder.stop();
                setMediaRecorder(null);
            }
            setIsRecording(false);
            if (animationRef.current) cancelAnimationFrame(animationRef.current);
            setVolumeLevel(1);
            // 结束录音时清除定时器
            if (recordTimerRef.current) clearInterval(recordTimerRef.current);
        }
    };

    // 1. 状态和方法（如未有则补充）
    const [imagePickerVisible, setImagePickerVisible] = useState(false);
    const openImagePicker = () => {
      setShowImagePicker(true);
      setTimeout(() => setImagePickerVisible(true), 10);
    };
    const closeImagePicker = () => {
      setImagePickerVisible(false);
      setTimeout(() => setShowImagePicker(false), 300);
      setSelectedPublicImages([]);
    };

    // 本地奶茶鼠表情GIF动图数组
    const NAICHASHU_EMOJIS = [
      '/emoji/naichashu/1.gif',
      '/emoji/naichashu/2.gif',
      '/emoji/naichashu/3.gif',
      '/emoji/naichashu/4.gif',
      '/emoji/naichashu/5.gif',
      '/emoji/naichashu/6.gif',
      '/emoji/naichashu/7.gif',
      '/emoji/naichashu/8.gif',
    ];

    // 替换原有 showNaichaPanel 相关逻辑为多分组切换
    const [showEmojiPanel, setShowEmojiPanel] = useState(false);
    const [activeEmojiGroup, setActiveEmojiGroup] = useState(EMOJI_GROUPS[0].key);

    // sendEmojiImage 支持 emoji 和图片，发送时通过 ws 发送 type: 'emoji' 或 type: 'image'
    const sendEmojiImage = (data) => {
      if (typeof data === 'string' && data.length <= 4) {
        // emoji
        const emojiMsg = {
          type: 'emoji',
          text: data,
          senderId: userId,
          receiverId: targetUserId,
          timestamp: new Date().toISOString()
        };
        ws && ws.readyState === 1 && ws.send(JSON.stringify(emojiMsg));
        setMessages(prev => [...prev, {
          id: Date.now() + Math.random(),
          type: 'emoji',
          sender: userId,
          isOwn: true,
          text: data,
          timestamp: new Date().toISOString()
        }]);
        setTimeout(() => { scrollToBottom(); }, 100);
      } else {
        // 图片
        const imgMsg = {
          type: 'image',
          fileUrl: data,
          senderId: userId,
          receiverId: targetUserId,
          timestamp: new Date().toISOString()
        };
        ws && ws.readyState === 1 && ws.send(JSON.stringify(imgMsg));
        setMessages(prev => [...prev, {
          id: Date.now() + Math.random(),
          type: 'image',
          sender: userId,
          isOwn: true,
          fileUrl: data,
          timestamp: new Date().toISOString()
        }]);
        setTimeout(() => { scrollToBottom(); }, 100);
      }
      // 不自动关闭面板
    };

    // 监听全局点击关闭表情面板
    useEffect(() => {
      if (!showEmojiPanel) return;
      const close = () => setShowEmojiPanel(false);
      window.addEventListener('click', close);
      return () => window.removeEventListener('click', close);
    }, [showEmojiPanel]);

    const [newMsgCount, setNewMsgCount] = useState(0);
    const [isAtBottom, setIsAtBottom] = useState(true);

    // 监听消息容器滚动，判断是否在底部
    useEffect(() => {
      const container = document.querySelector(`.${style.messagesContainer}`);
      if (!container) return;
      const handleScroll = () => {
        const atBottom = container.scrollHeight - container.scrollTop - container.clientHeight < 10;
        setIsAtBottom(atBottom);
        if (atBottom) setNewMsgCount(0);
      };
      container.addEventListener('scroll', handleScroll);
      return () => container.removeEventListener('scroll', handleScroll);
    }, []);

    // 监听新消息，未在底部且为对方消息时计数
    useEffect(() => {
      if (!isAtBottom) {
        const lastMsg = messages[messages.length - 1];
        if (lastMsg && !lastMsg.isOwn) {
          setNewMsgCount(c => c + 1);
        }
      }
    }, [messages]);

    const [voiceTextMap, setVoiceTextMap] = useState({}); // {msgId: '转写内容'}
    const [voiceTranscribing, setVoiceTranscribing] = useState({}); // {msgId: true/false}
    const [voiceTextShowMap, setVoiceTextShowMap] = useState({}); // {msgId: true/false}

    const [showCamera, setShowCamera] = useState(false);
    
    // 语音转文字相关状态
    const [isVoiceToTextMode, setIsVoiceToTextMode] = useState(false);
    const [voiceToTextResult, setVoiceToTextResult] = useState('');
    const [isTranscribing, setIsTranscribing] = useState(false);
    const [transcriptionError, setTranscriptionError] = useState('');

    const [fakeVolume, setFakeVolume] = useState(1);
    const [fakeSeconds, setFakeSeconds] = useState(0);

    // 语音转文字功能 - 使用Web Speech API
    const startVoiceToText = () => {
        if (isTranscribing) return;
        setIsTranscribing(true);
        setTranscriptionError('');
        setVoiceToTextResult('');
        setFakeVolume(1);
        setFakeSeconds(0);

        // 音量动画
        let volTimer = setInterval(() => {
            setFakeVolume(Math.floor(Math.random() * 7) + 1); // 1~7
        }, 120);

        // 秒数计时
        let sec = 0;
        let secTimer = setInterval(() => {
            sec += 1;
            setFakeSeconds(sec);
        }, 1000);

        setTimeout(() => {
            clearInterval(volTimer);
            clearInterval(secTimer);
            setIsTranscribing(false);
            setIsVoiceToTextMode(false);
            setInputMessage('请求试音。');
            setTimeout(() => {
                const input = document.querySelector(`.${style.wxInput}`);
                if (input) input.focus();
            }, 100);
        }, 2000);
    };

    // 发送语音转文字结果
    const sendVoiceToTextResult = () => {
        if (!voiceToTextResult.trim()) return;
        
        setInputMessage(voiceToTextResult);
        setVoiceToTextResult('');
        setIsVoiceToTextMode(false);
    };

    // 聊天天数计算
    function getChatDays(messages) {
      if (!messages.length) return 0;
      const first = new Date(messages[0].timestamp);
      const last = new Date(messages[messages.length - 1].timestamp);
      return Math.max(1, Math.ceil((last - first) / (1000 * 60 * 60 * 24)) + 1);
    }

    // 聊天频率统计数据
    function getChatFrequencyData(messages) {
      // 生成近30天日期
      const days = [];
      const today = new Date();
      for (let i = 29; i >= 0; i--) {
        const d = new Date(today);
        d.setDate(today.getDate() - i);
        days.push(d.toISOString().slice(0, 10));
      }
      // 统计每天消息数
      const freqMap = {};
      messages.forEach(msg => {
        const day = new Date(msg.timestamp).toISOString().slice(0, 10);
        freqMap[day] = (freqMap[day] || 0) + 1;
      });
      // 组装数据
      const data = days.map(day => ({
        date: day.slice(5), // MM-DD
        count: freqMap[day] || 0
      }));
      return data;
    }
    const freqData = getChatFrequencyData(messages);
    const totalCount = messages.length;
    const activeDays = freqData.filter(d => d.count > 0).length;
    const last7 = freqData.slice(-7).reduce((a, b) => a + b.count, 0);
    const prev7 = freqData.slice(-14, -7).reduce((a, b) => a + b.count, 0);
    const percent7 = prev7 === 0 ? 0 : ((last7 - prev7) / prev7 * 100).toFixed(1);

    const [showFiremanPicker, setShowFiremanPicker] = useState(false);
    const [firemanKey, setFiremanKey] = useState('default');
    const [pendingFiremanKey, setPendingFiremanKey] = useState(firemanKey);

    useEffect(() => {
      if (!showFiremanPicker) return;
      const handleEsc = (e) => {
        if (e.key === 'Escape') setShowFiremanPicker(false);
      };
      window.addEventListener('keydown', handleEsc);
      return () => window.removeEventListener('keydown', handleEsc);
    }, [showFiremanPicker]);

    // 打开弹窗时同步pendingFiremanKey
    useEffect(() => {
      if (showFiremanPicker) setPendingFiremanKey(firemanKey);
    }, [showFiremanPicker, firemanKey]);

    // 2. 在组件内部定义播放状态和ref
    const [playingVideoId, setPlayingVideoId] = useState(null);
    const videoRefs = useRef({});

    // 1. 在组件状态中添加控制弹窗显示的状态
    const [showLocationModal, setShowLocationModal] = useState(false);

    // 在Liao组件内添加地图初始化逻辑
    useEffect(() => {
      if (showLocationModal && window.qq && window.qq.maps) {
        // 延迟初始化地图，确保DOM元素已经渲染
        const timer = setTimeout(() => {
          const container = document.getElementById('tmapContainer');
          if (container) {
            try {
              // 初始化地图
              const map = new window.qq.maps.Map(container, {
                center: new window.qq.maps.LatLng(38.8767, 115.4971), // 保定理工学院附近
                zoom: 15,
                mapTypeId: window.qq.maps.MapTypeId.ROADMAP
              });

              // 添加两个头像标记
              const markers = [
                {
                  position: new window.qq.maps.LatLng(38.8767, 115.4971),
                  icon: '/个人头像.png',
                },
                {
                  position: new window.qq.maps.LatLng(38.8772, 115.4980),
                  icon: '/微信头像.jpg',
                },
              ];

              markers.forEach((m, index) => {
                // 区分描边颜色：第一个绿色，第二个蓝色
                const borderColor = index === 0 ? '#1aad19' : '#4fc3f7';
                const html = `
                  <div style="
                    width:48px;
                    height:48px;
                    border-radius:50%;
                    overflow:hidden;
                    border:3px solid ${borderColor};
                    box-shadow:0 2px 8px #0003;
                    background:#fff;
                    display:flex;
                    align-items:center;
                    justify-content:center;
                  ">
                    <img src="${m.icon}" style="width:100%;height:100%;object-fit:cover;border-radius:50%;" />
                  </div>
                `;
                new window.qq.maps.Marker({
                  position: m.position,
                  map: map,
                  content: html,
                  title: index === 0 ? '我' : '对方'
                });
              });
            } catch (error) {
              console.error('地图初始化失败:', error);
            }
          }
        }, 100);
        return () => clearTimeout(timer);
      }
    }, [showLocationModal]);

    // 发送位置共享消息
    const sendLocationShare = () => {
      const msg = {
        type: 'location',
        senderId: userId,
        receiverId: targetUserId,
        timestamp: new Date().toISOString(),
      };
      ws && ws.readyState === 1 && ws.send(JSON.stringify(msg));
      setMessages(prev => [...prev, {
        ...msg,
        id: Date.now() + Math.random(),
        isOwn: true
      }]);
    };

    // 消息变化时自动滚动到底部（包括首次进入页面）
    useEffect(() => {
      scrollToBottom();
    }, [messages]);

    return (
        <div className={style.chatContainer} style={{ display: 'flex', flexDirection: 'column', height: '100vh' }}>
            {/* 表头始终在最上方 */}
            <div className={style.header}>
              <span className={style.title}>{targetUserName}</span>
              <div className={style.sparkWrap}>
                <img src="/spark.png" className={style.sparkIcon} alt="火苗" />
                <span className={style.daysText}>{getChatDays(messages)}</span>
              </div>
              <span className={style.moreBtn}>···</span>
            </div>
            {/* 消息区自适应高度 */}
            <div className={style.messagesContainer} style={{ flex: 1, overflowY: 'auto' }}>
                {messages.length === 0 ? (
                    <div className={style.emptyState}>

                    </div>
                ) : (
                    messages.map((message, idx) => {
                        const prevMsg = messages[idx - 1];
                        let showTimeDivider = false;
                        // 取时间戳（Date对象），兼容timestamp为字符串或Date对象
                        const currTime = new Date(message.timestamp ? message.timestamp : Date.now());
                        let prevTime = null;
                        if (prevMsg) {
                            prevTime = new Date(prevMsg.timestamp ? prevMsg.timestamp : Date.now());
                        }
                        // 只在第一条或与上一条消息间隔超过5分钟时显示
                        if (
                            idx === 0 ||
                            (prevTime && Math.abs(currTime - prevTime) > 5 * 60 * 1000)
                        ) {
                            showTimeDivider = true;
                        }
                        const checked = selectedMessages.includes(message.id);
                        return (
                            <React.Fragment key={message.id}>
                                {showTimeDivider && (
                                    <div
                                        style={{
                                            textAlign: 'center',
                                            color: '#bbb',
                                            fontSize: 16,
                                            margin: '18px 0 10px 0',
                                            letterSpacing: 1,
                                            fontWeight: 400,
                                            userSelect: 'none',
                                        }}
                                    >
                                        {currTime.getHours().toString().padStart(2, '0') + ':' + currTime.getMinutes().toString().padStart(2, '0')}
                                    </div>
                                )}
                                <div
                                    className={`${style.message} ${message.isOwn ? style.ownMessage : style.otherMessage}`}
                                    onClick={() => {
                                        if (isSelectMode) {
                                            setSelectedMessages(checked
                                                ? selectedMessages.filter(id => id !== message.id)
                                                : [...selectedMessages, message.id]);
                                        }
                                    }}
                                    style={{
                                        ...(isSelectMode && checked ? { background: '#e1f3d8' } : {}),
                                        position: 'relative',
                                        ...(isSelectMode ? { paddingLeft: 32 } : {})
                                    }}
                                >
                                    {isSelectMode && (
                                        <span
                                            style={{
                                                display: 'flex',
                                                alignItems: 'center',
                                                justifyContent: 'center'
                                            }}
                                        >
                                            {checked && (
                                                <svg width="14" height="14" viewBox="0 0 14 14">
                                                    <polyline points="3,7 6,10 11,4" style={{ fill: 'none', stroke: '#fff', strokeWidth: 2 }} />
                                                </svg>
                                            )}
                                        </span>
                                    )}
                                    <div style={isSelectMode ? { marginLeft: 28 } : {}}>
                                        <div className={style.messageRow}>
                                            {/* 左侧头像 */}
                                            {!message.isOwn && (
                                                <img src="/微信头像.jpg" alt="avatar" className={style.avatar} />
                                            )}
                                            <div className={style.bubbleWrap}>
                                                <div className={style.bubbleBox}>
                                                    {/* 只对非图片消息显示气泡箭头和背景 */}
                                                    {message.type !== 'image' && !message.isOwn && <span className={style.bubbleArrowLeft}></span>}
                                                    {message.type === 'voice' ? (
                                                        <div>
                                                            <div
                                                                style={{
                                                                    display: 'flex',
                                                                    flexDirection: message.isOwn ? 'row-reverse' : 'row',
                                                                    alignItems: 'center',
                                                                    gap: 4,
                                                                }}
                                                            >
                                                                <div
                                                                    className={style.voiceBubble}
                                                                    style={{
                                                                        width: Math.min(60 + (message.duration || 1) * 8, 220),
                                                                        boxShadow: '0 2px 8px rgba(0,0,0,0.08)',
                                                                        borderRadius: 16,
                                                                        padding: '0 16px',
                                                                        background: message.isOwn ? '#e1f3d8' : '#fff',
                                                                    }}
                                                                    onClick={() => !isSelectMode && handleVoiceClick(message)}
                                                                >
                                                                    {/* 红点 */}
                                                                    {!message.isOwn && !voiceReadMap[message.id] && <span className={style.voiceUnreadDot}></span>}
                                                                    {/* 显示录音时长 */}
                                                                    <span className={style.voiceDuration}>{message.duration || 1}″</span>
                                                                    <span className={style.voiceIcon}>
                                                                        <svg width="22" height="22" viewBox="0 0 22 22"><path d="M6 11a5 5 0 0 1 5-5v2a3 3 0 0 0-3 3h-2zm5-5v2a5 5 0 0 1 5 5h2a7 7 0 0 0-7-7zm0 10v2a7 7 0 0 0 7-7h-2a5 5 0 0 1-5 5z" fill="#222"/></svg>
                                                                    </span>
                                                                    {playingAudioId === message.id && <span className={style.voicePlayingBar}></span>}
                                                                </div>
                                                                <span
                                                                    style={{
                                                                        display: 'flex',
                                                                        alignItems: 'center',
                                                                        gap: 2,
                                                                        color: '#06c05f',
                                                                        fontSize: 15,
                                                                        fontWeight: 400,
                                                                        cursor: 'pointer',
                                                                        userSelect: 'none',
                                                                        padding: '0 8px',
                                                                        lineHeight: '28px',
                                                                        borderRadius: 18,
                                                                        background: 'transparent',
                                                                        transition: 'background 0.15s, color 0.15s',
                                                                        marginLeft: message.isOwn ? 0 : 2,
                                                                        marginRight: message.isOwn ? 2 : 0,
                                                                        whiteSpace: 'nowrap',
                                                                    }}
                                                                    onMouseOver={e => {
                                                                        e.currentTarget.style.background = '#e6f7ec';
                                                                        e.currentTarget.style.color = '#04b96b';
                                                                    }}
                                                                    onMouseOut={e => {
                                                                        e.currentTarget.style.background = 'transparent';
                                                                        e.currentTarget.style.color = '#06c05f';
                                                                    }}
                                                                    onClick={async (e) => {
                                                                        e.stopPropagation();
                                                                        console.log('[转文字按钮点击]', {
                                                                            messageId: message.id,
                                                                            messageType: message.type,
                                                                            voiceTextMap,
                                                                            voiceTranscribing,
                                                                            voiceTextShowMap
                                                                        });
                                                                        if (voiceTextMap[message.id]) {
                                                                            setVoiceTextShowMap(v => ({ ...v, [message.id]: !v[message.id] }));
                                                                            return;
                                                                        }
                                                                        if (voiceTranscribing[message.id]) return;
                                                                        setVoiceTranscribing(v => ({ ...v, [message.id]: true }));
                                                                        // 假数据模拟转写，实际应调用API
                                                                        setTimeout(() => {
                                                                            console.log('[转写完成]', { messageId: message.id });
                                                                            setVoiceTextMap(v => ({ ...v, [message.id]: '请求试音，请求试音！' })); // 固定内容
                                                                            setVoiceTranscribing(v => ({ ...v, [message.id]: false }));
                                                                            setVoiceTextShowMap(v => ({ ...v, [message.id]: true }));
                                                                        }, 1200);
                                                                    }}
                                                                >
                                                                    {voiceTextMap[message.id] ? (voiceTextShowMap[message.id] ? '收起转文字' : '转文字') : (voiceTranscribing[message.id] ? '转写中...' : '转文字')}
                                                                </span>
                                                            </div>
                                                            {/* 转写结果 */}
                                                            {voiceTextMap[message.id] && voiceTextShowMap[message.id] && (
                                                                <div style={{ background: '#f6f6f6', borderRadius: 8, padding: '8px 12px', marginTop: 4, color: '#222', fontSize: 15 }}>
                                                                    {voiceTextMap[message.id]}
                                                                </div>
                                                            )}
                                                        </div>
                                                    ) : message.type === 'transfer' ? (
                                                        <TransferCard message={message} onClick={() => navigate('/transfer', { state: { ...message } })} />
                                                    ) : message.type === 'image' ? (
                                                        <div className={style.messageContent + ' ' + style.image}>
                                                            <img
                                                                src={message.fileUrl}
                                                                alt="img"
                                                                style={{ maxWidth: 120, maxHeight: 120, borderRadius: 8, cursor: 'pointer' }}
                                                                onClick={() => {
                                                                  const media = messages.filter(m => m.type === 'image' || m.type === 'video').map(m => ({ type: m.type, url: m.fileUrl }));
                                                                  setPreviewImages(media);
                                                                  setPreviewIndex(media.findIndex(m => m.url === message.fileUrl));
                                                                }}
                                                            />
                                                        </div>
                                                    ) : message.type === 'video' ? (
                                                        <div className={style.messageContent + ' ' + style.video} style={{position:'relative'}}>
                                                            <video
                                                                ref={el => videoRefs.current[message.id] = el}
                                                                src={message.fileUrl}
                                                                style={{ maxWidth: 120, maxHeight: 120, borderRadius: 8, objectFit: 'cover', background: 'none', display: 'block' }}
                                                                controls={false}
                                                                poster={message.coverUrl}
                                                                onEnded={() => setPlayingVideoId(null)}
                                                            />
                                                            {playingVideoId !== message.id && (
                                                                <button
                                                                    className={style.videoPlayBtn}
                                                                    style={{
                                                                        position: 'absolute',
                                                                        left: '50%',
                                                                        top: '50%',
                                                                        transform: 'translate(-50%, -50%)',
                                                                        background: 'rgba(0,0,0,0.5)',
                                                                        border: 'none',
                                                                        borderRadius: '50%',
                                                                        width: 48,
                                                                        height: 48,
                                                                        display: 'flex',
                                                                        alignItems: 'center',
                                                                        justifyContent: 'center',
                                                                        cursor: 'pointer',
                                                                        zIndex: 2
                                                                    }}
                                                                    onClick={e => {
                                                                        e.stopPropagation();
                                                                        setPlayingVideoId(message.id);
                                                                        const v = videoRefs.current[message.id];
                                                                        if (v) {
                                                                            v.play();
                                                                            if (v.requestFullscreen) v.requestFullscreen();
                                                                        }
                                                                    }}
                                                                >
                                                                    <svg width="32" height="32" viewBox="0 0 32 32"><circle cx="16" cy="16" r="16" fill="#000" fillOpacity="0.5"/><polygon points="13,10 24,16 13,22" fill="#fff"/></svg>
                                                                </button>
                                                            )}
                                                        </div>
                                                    ) : message.type === 'emoji' ? (
                                                        <div className={style.messageContent} style={{ fontSize: 40, background: 'none', boxShadow: 'none', padding: 0 }}>
                                                            <span>{message.text}</span>
                                                        </div>
                                                    ) : message.type === 'location' ? (
                                                        <div
                                                            className={style.locationBubble}
                                                            onClick={() => setShowLocationModal(true)}
                                                            style={{
                                                                display: 'flex', alignItems: 'center', cursor: 'pointer', color: '#06c05f', background: '#e6f7ec', borderRadius: 10, padding: '8px 16px', minWidth: 120
                                                            }}
                                                        >
                                                            <svg width="28" height="28" viewBox="0 0 32 32" fill="none"><path d="M16 28s8-8.5 8-14A8 8 0 1 0 8 14c0 5.5 8 14 8 14z" stroke="#06c05f" strokeWidth="2" fill="#fff"/><circle cx="16" cy="14" r="3" fill="#06c05f"/></svg>
                                                            <span style={{marginLeft: 8, fontWeight: 500}}>点击查看位置共享</span>
                                                        </div>
                                                    ) : (
                                                        <div className={style.messageContent}>
                                                            <div className={style.messageText}>{message.text}</div>
                                                        </div>
                                                    )}
                                                    {/* 只对非图片消息显示气泡箭头和背景 */}
                                                    {message.type !== 'image' && message.isOwn && <span className={style.bubbleArrowRight}></span>}
                                                </div>
                                            </div>
                                            {/* 右侧头像 */}
                                            {message.isOwn && (
                                                <img src="/个人头像.png" alt="avatar" className={style.avatar} />
                                            )}
                                        </div>
                                    </div>
                                </div>
                            </React.Fragment>
                        );
                    })
                )}
                {/* 删除原本在消息列表下方显示的小火人 */}
                {/* {messages.length > 0 && messages[messages.length - 1].isOwn && (
                  <div style={{
                    display: 'flex',
                    flexDirection: 'row',
                    justifyContent: 'flex-end',
                    marginRight: 10,
                    marginTop: 8,
                    marginBottom: 8,
                  }}>
                    <div>
                      <FireMan />
                    </div>
                  </div>
                )} */}
                <div ref={messagesEndRef} />
            </div>

            {isSelectMode && (
                <div style={{ position: 'fixed', bottom: 0, left: 0, right: 0, background: '#fff', borderTop: '1px solid #eee', padding: 12, display: 'flex', justifyContent: 'space-between', zIndex: 2000 }}>
                    <button onClick={() => setIsSelectMode(false)}>取消</button>
                    <button
                        style={{ color: '#fff', background: '#ff3b30', border: 'none', borderRadius: 6, padding: '6px 18px', fontWeight: 500, marginLeft: 12 }}
                        disabled={selectedMessages.length === 0}
                        onClick={() => {
                            setMessages(messages.filter(m => !selectedMessages.includes(m.id)));
                            setSelectedMessages([]);
                            setIsSelectMode(false);
                        }}
                    >删除已选（{selectedMessages.length}）</button>
                </div>
            )}

            {/* 新消息提醒按钮 */}
            {newMsgCount > 0 && !isAtBottom && (
              <div
                style={{
                  position: 'fixed', right: 24, bottom: 90, background: '#06c05f', color: '#fff',
                  borderRadius: 18, padding: '8px 18px', fontSize: 16, cursor: 'pointer', zIndex: 3000,
                  boxShadow: '0 2px 8px #0002'
                }}
                onClick={() => {
                  const container = document.querySelector(`.${style.messagesContainer}`);
                  if (container) {
                    container.scrollTo({
                      top: container.scrollHeight,
                      behavior: 'smooth'
                    });
                  }
                  setNewMsgCount(0);
                  setIsAtBottom(true);
                }}
              >
                最新消息{newMsgCount}条
              </div>
            )}

            {/* 滑到底部按钮 */}
            {!isAtBottom && newMsgCount === 0 && (
              <div
                style={{
                  position: 'fixed', right: 24, bottom: 40, background: '#06c05f', color: '#fff',
                  borderRadius: 18, padding: '8px 18px', fontSize: 16, cursor: 'pointer', zIndex: 3000,
                  boxShadow: '0 2px 8px #0002'
                }}
                onClick={() => {
                  const container = document.querySelector(`.${style.messagesContainer}`);
                  if (container) {
                    container.scrollTo({
                      top: container.scrollHeight,
                      behavior: 'smooth'
                    });
                  }
                  setIsAtBottom(true);
                }}
              >
                滑到底部
              </div>
            )}

            {/* 底部区域：输入区+表情面板 */}
            <div style={{ background: '#fff', borderTop: '1px solid #eee' }}>
                {/* 输入区 */}
                <div className={style.inputBarWx}>
                    {isVoiceMode ? (
                        <>
                            <button
                                className={style.wxIconBtn}
                                type="button" 
                                title="切换到文字输入"
                                onClick={() => {
                                    setIsVoiceMode(false);
                                    setIsVoiceToTextMode(false);
                                }}
                            >
                                <span role="img" aria-label="keyboard">⌨️</span>
                            </button>
                            <button
                                className={style.wxVoiceBtn}
                                type="button"
                                onClick={handleVoiceBtnClick}
                            >
                                {isRecording ? '结束录音' : '按住 说话'}
                            </button>
                        </>
                    ) : isVoiceToTextMode ? (
                        <>
                            <button
                                className={style.wxIconBtn}
                                type="button"
                                title="切换到文字输入"
                                onClick={() => {
                                    setIsVoiceToTextMode(false);
                                    setVoiceToTextResult('');
                                }}
                            >
                                <span role="img" aria-label="keyboard">⌨️</span>
                            </button>
                            <div 
                                className={style.wxInput} 
                                style={{ 
                                    display: 'flex', 
                                    alignItems: 'center', 
                                    justifyContent: 'space-between',
                                    background: '#f8f8f8',
                                    color: '#666',
                                    cursor: !isTranscribing ? 'pointer' : 'default'
                                }}
                                onClick={() => {
                                    if (!isTranscribing && !isRecording) {
                                        startVoiceToText();
                                    }
                                }}
                            >
                                <span>{isTranscribing ? '正在转文字...' : voiceToTextResult || '点击开始语音输入'}</span>
                                {voiceToTextResult && !isTranscribing && (
                                    <button
                                        style={{
                                            background: '#06c05f',
                                            color: '#fff',
                                            border: 'none',
                                            borderRadius: 4,
                                            padding: '4px 8px',
                                            fontSize: 12,
                                            cursor: 'pointer'
                                        }}
                                        onClick={(e) => {
                                            e.stopPropagation();
                                            sendVoiceToTextResult();
                                        }}
                                    >
                                        发送
                                    </button>
                                )}
                            </div>
                        </>
                    ) : (
                        <>
                            <button
                                className={style.wxIconBtn}
                                type="button"
                                title="语音"
                                onClick={() => setIsVoiceMode(true)}
                            >
                                <span role="img" aria-label="voice">🎤</span>
                            </button>
                            <input
                                type="text"
                                value={inputMessage}
                                onChange={(e) => setInputMessage(e.target.value)}
                                className={style.wxInput}
                                disabled={!isConnected}
                            />
                        </>
                    )}
                    <button
                        className={style.wxIconBtn}
                        tabIndex={-1}
                        type="button"
                        title="表情"
                        onClick={e => {
                            e.stopPropagation();
                            setShowEmojiPanel(v => !v);
                        }}
                    >
                        <span role="img" aria-label="emoji">😀</span>
                    </button>
                    <button
                        className={inputMessage.trim() ? style.sendBtn : style.wxIconBtn}
                        tabIndex={-1}
                        type="button"
                        title={inputMessage.trim() ? "发送" : "更多"}
                        onClick={inputMessage.trim() ? sendMessage : () => setShowPlusPanel(v => !v)}
                        disabled={!isConnected || (inputMessage.trim() && !inputMessage)}
                    >
                        {inputMessage.trim() ? (
                            <span className={style.sendBtnText}>发送</span>
                        ) : (
                            <span role="img" aria-label="plus">➕</span>
                        )}
                    </button>
                </div>
                {/* 表情面板直接在输入区下方 */}
                {showEmojiPanel && (
                    <div
                        className={style.emojiPanel}
                        style={{
                            background: '#fff',
                            boxShadow: '0 -2px 12px #eee',
                            borderTopLeftRadius: 18,
                            borderTopRightRadius: 18,
                            minHeight: 260,
                            maxHeight: 340,
                            overflowY: 'auto',
                            transition: 'all 0.2s cubic-bezier(.34,1.56,.64,1)'
                        }}
                        onClick={e => e.stopPropagation()}
                    >
                        {/* 顶部分组切换栏 */}
                        <div style={{ display: 'flex', alignItems: 'center', borderBottom: '1px solid #eee', padding: '0 10px 6px 10px', marginBottom: 8 }}>
                            {EMOJI_GROUPS.map(group => (
                                <div
                                    key={group.key}
                                    style={{
                                        marginRight: 12,
                                        cursor: 'pointer',
                                        borderBottom: activeEmojiGroup === group.key ? '2px solid #06c05f' : '2px solid transparent',
                                        padding: '2px 0',
                                        display: 'flex', flexDirection: 'column', alignItems: 'center',
                                        fontSize: 12, color: activeEmojiGroup === group.key ? '#06c05f' : '#888', fontWeight: 500
                                    }}
                                    onClick={() => setActiveEmojiGroup(group.key)}
                                >
                                    <img src={group.images[0]} alt={group.name} style={{ width: 32, height: 32, borderRadius: 6, marginBottom: 2, border: activeEmojiGroup === group.key ? '2px solid #06c05f' : '2px solid #eee', background: '#f6f6f6' }} />
                                    {group.name}
                                </div>
                            ))}
                        </div>
                        {/* 当前分组内容区 */}
                        <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap', padding: '0 12px 12px 12px' }}>
                            {EMOJI_GROUPS.find(g => g.key === activeEmojiGroup).images.map(url => (
                                <img key={url} src={url} alt="emoji" style={{ width: 48, height: 48, borderRadius: 8, cursor: 'pointer', background: '#f6f6f6' }} onClick={() => sendEmojiImage(url)} />
                            ))}
                        </div>
                        {/* 关闭按钮 */}
                        <button style={{ position: 'absolute', right: 12, bottom: 12, background: '#fff', border: 'none', borderRadius: '50%', width: 32, height: 32, boxShadow: '0 2px 8px #eee', cursor: 'pointer', fontSize: 18 }} onClick={() => setShowEmojiPanel(false)}>×</button>
                    </div>
                )}
            </div>

            {showPlusPanel && (
                <div className={style.plusPanelMask} onClick={() => setShowPlusPanel(false)}>
                    <div className={style.plusPanel} onClick={e => e.stopPropagation()}>
                        <div className={style.plusPanelRow}>
                            <div className={style.plusPanelItem} onClick={openImagePicker}>
                                <span className={style.plusPanelIcon}>
                                    {/* 相册 */}
                                    <svg width="32" height="32" viewBox="0 0 32 32" fill="none"><rect x="5" y="8" width="22" height="16" rx="4" fill="#fff" stroke="#111" strokeWidth="2"/><circle cx="11" cy="14" r="2" fill="#111"/><path d="M7 22l5-6 4 5 5-7 4 8" stroke="#111" strokeWidth="2" strokeLinejoin="round" fill="none"/></svg>
                                </span>
                                <div className={style.plusPanelText}>相册</div>
                            </div>
                            <input
                                type="file"
                                ref={fileInputRef}
                                style={{ display: 'none' }}
                                accept="image/*,video/*"
                                multiple
                                onChange={e => {
                                    setSelectedFiles(Array.from(e.target.files));
                                    setShowPlusPanel(false);
                                }}
                            />
                            <div className={style.plusPanelItem} onClick={() => { setShowCamera(true); setShowPlusPanel(false); }}>
                                <span className={style.plusPanelIcon}>
                                    {/* 拍摄 */}
                                    <svg width="32" height="32" viewBox="0 0 32 32" fill="none"><rect x="7" y="11" width="18" height="12" rx="3" fill="#fff" stroke="#111" strokeWidth="2"/><circle cx="16" cy="17" r="4" fill="#111"/><rect x="12" y="7" width="8" height="4" rx="2" fill="#111"/></svg>
                                </span>
                                <div className={style.plusPanelText}>拍摄</div>
                            </div>
                            <div className={style.plusPanelItem}>
                                <span className={style.plusPanelIcon}
                                onClick={() => { setShowCallSheet(true); setShowPlusPanel(false); }}
                                >
                                    {/* 视频通话 */}
                                    <svg width="32" height="32" viewBox="0 0 32 32" fill="none"><rect x="6" y="10" width="14" height="12" rx="3" fill="#fff" stroke="#111" strokeWidth="2"/><rect x="22" y="13" width="4" height="6" rx="1" fill="#111"/></svg>
                                </span>
                                <div className={style.plusPanelText}>视频通话</div>
                            </div>
                            <div className={style.plusPanelItem} onClick={() => { sendLocationShare(); setShowPlusPanel(false); }}>
                                <span className={style.plusPanelIcon}>
                                    {/* 位置 */}
                                    <svg width="32" height="32" viewBox="0 0 32 32" fill="none"><path d="M16 28s8-8.5 8-14A8 8 0 1 0 8 14c0 5.5 8 14 8 14z" stroke="#111" strokeWidth="2" fill="#fff"/><circle cx="16" cy="14" r="3" fill="#111"/></svg>
                                </span>
                                <div className={style.plusPanelText}>发送位置</div>
                            </div>
                        </div>
                        <div className={style.plusPanelRow}>
                            <div className={style.plusPanelItem}>
                                <span className={style.plusPanelIcon}>
                                    {/* 红包 */}
                                    <svg width="32" height="32" viewBox="0 0 32 32" fill="none"><rect x="7" y="8" width="18" height="16" rx="4" fill="#fff" stroke="#111" strokeWidth="2"/><path d="M7 12h18" stroke="#111" strokeWidth="2"/><circle cx="16" cy="18" r="3" fill="#111"/></svg>
                                </span>
                                <div className={style.plusPanelText}>红包</div>
                            </div>
                            <div className={style.plusPanelItem}>
                                <span className={style.plusPanelIcon}>
                                    {/* 礼物 */}
                                    <svg width="32" height="32" viewBox="0 0 32 32" fill="none"><rect x="7" y="12" width="18" height="12" rx="3" fill="#fff" stroke="#111" strokeWidth="2"/><path d="M7 18h18" stroke="#111" strokeWidth="2"/><rect x="13" y="7" width="6" height="6" rx="3" fill="#fff" stroke="#111" strokeWidth="2"/><path d="M16 12v12" stroke="#111" strokeWidth="2"/></svg>
                                </span>
                                <div className={style.plusPanelText}>礼物</div>
                            </div>
                            <div className={style.plusPanelItem} onClick={() => navigate('/transfer', { state: { targetUserId, targetUserName: '爱吃柠檬', targetUserAvatar: '/微信头像.jpg' } })} style={{cursor:'pointer'}}>
                                <span className={style.plusPanelIcon}>
                                    {/* 转账 */}
                                    <svg width="32" height="32" viewBox="0 0 32 32" fill="none"><rect x="7" y="12" width="18" height="8" rx="3" fill="#fff" stroke="#111" strokeWidth="2"/><path d="M13 16h6" stroke="#111" strokeWidth="2"/><path d="M19 14l3 2-3 2" stroke="#111" strokeWidth="2" strokeLinejoin="round"/></svg>
                                </span>
                                <div className={style.plusPanelText}>转账</div>
                            </div>
                            <div className={style.plusPanelItem} onClick={() => {
                                setShowPlusPanel(false);
                                setIsVoiceToTextMode(true);
                            }}>
                                <span className={style.plusPanelIcon}>
                                    <div style={{background:'#fff',borderRadius:'10px',width:'32px',height:'32px',display:'flex',alignItems:'center',justifyContent:'center',boxShadow:'0 1px 3px #eee'}}>
                                        <span role="img" aria-label="voice" style={{fontSize:'20px'}}>🎤</span>
                                    </div>
                                </span>
                                <div className={style.plusPanelText}>语音输入</div>
                            </div>
                        </div>
                        <div className={style.plusPanelDots}>
                            <span className={style.dotActive}></span>
                            <span className={style.dot}></span>
                        </div>
                    </div>
                </div>
            )}

            {isRecording && (
                <div className={style.recordMask}>
                    <div className={style.recordModal}>
                        {/* 错误提示 */}
                        {recordError && <div style={{color:'#ff3b30',marginBottom:8,fontSize:14}}>{recordError}</div>}
                        <div className={style.recordMicWrap}>
                            <svg className={style.recordMic} width="32" height="32" viewBox="0 0 32 32"><rect x="10" y="4" width="12" height="16" rx="6" fill="#fff" stroke="#06c05f" strokeWidth="2"/><rect x="14" y="20" width="4" height="6" rx="2" fill="#06c05f"/><rect x="8" y="26" width="16" height="3" rx="1.5" fill="#06c05f"/></svg>
                            <div className={style.recordVolumeBar}>
                                {[...Array(7)].map((_, i) => (
                                    <div
                                        key={i}
                                        className={style.recordVolumeCol}
                                        style={{
                                            height: `${10 + (i + 1) * 6}px`,
                                            opacity: i < volumeLevel ? 1 : 0.25
                                        }}
                                    />
                                ))}
                            </div>
                        </div>
                        <div className={style.recordText}>松开 结束</div>
                        <div className={style.recordTime}>{recordTime}"</div>
                    </div>
                </div>
            )}

            {/* 语音转文字识别界面 */}
            {isTranscribing && isVoiceToTextMode && (
                <div className={style.recordMask}>
                    <div className={style.recordModal}>
                        <div className={style.recordMicWrap}>
                            <svg className={style.recordMic} width="32" height="32" viewBox="0 0 32 32"><rect x="10" y="4" width="12" height="16" rx="6" fill="#fff" stroke="#06c05f" strokeWidth="2"/><rect x="14" y="20" width="4" height="6" rx="2" fill="#06c05f"/><rect x="8" y="26" width="16" height="3" rx="1.5" fill="#06c05f"/></svg>
                            <div className={style.recordVolumeBar}>
                                {[...Array(7)].map((_, i) => (
                                    <div
                                        key={i}
                                        className={style.recordVolumeCol}
                                        style={{
                                            height: `${10 + (i + 1) * 6}px`,
                                            opacity: i < fakeVolume ? 1 : 0.25
                                        }}
                                    />
                                ))}
                            </div>
                        </div>
                        <div className={style.recordText}>正在识别语音...</div>
                        <div className={style.recordTime}>{fakeSeconds}"</div>
                        {transcriptionError && (
                            <div style={{color:'#ff3b30',marginTop:8,fontSize:14}}>{transcriptionError}</div>
                        )}
                    </div>
                </div>
            )}

            {/* 语音转文字结果界面 */}
            {isVoiceToTextMode && voiceToTextResult && !isTranscribing && (
                <div style={{
                    position: 'fixed',
                    top: 0,
                    left: 0,
                    right: 0,
                    bottom: 0,
                    background: 'rgba(0,0,0,0.8)',
                    zIndex: 3000,
                    display: 'flex',
                    alignItems: 'center',
                    justifyContent: 'center'
                }}>
                    <div style={{
                        background: '#fff',
                        borderRadius: 16,
                        padding: 24,
                        maxWidth: '80%',
                        width: 400,
                        textAlign: 'center'
                    }}>
                        <div style={{ fontSize: 18, fontWeight: 600, marginBottom: 16 }}>
                            语音转文字结果
                        </div>
                        <div style={{
                            background: '#f8f8f8',
                            padding: 16,
                            borderRadius: 8,
                            marginBottom: 20,
                            fontSize: 16,
                            lineHeight: 1.5,
                            minHeight: 60,
                            display: 'flex',
                            alignItems: 'center',
                            justifyContent: 'center'
                        }}>
                            {voiceToTextResult}
                        </div>
                        <div style={{ display: 'flex', gap: 12, justifyContent: 'center' }}>
                            <button
                                style={{
                                    background: '#f0f0f0',
                                    color: '#666',
                                    border: 'none',
                                    borderRadius: 8,
                                    padding: '12px 24px',
                                    fontSize: 16,
                                    cursor: 'pointer'
                                }}
                                onClick={() => {
                                    setVoiceToTextResult('');
                                    setIsVoiceToTextMode(false);
                                }}
                            >
                                重新录制
                            </button>
                            <button
                                style={{
                                    background: '#06c05f',
                                    color: '#fff',
                                    border: 'none',
                                    borderRadius: 8,
                                    padding: '12px 24px',
                                    fontSize: 16,
                                    cursor: 'pointer'
                                }}
                                onClick={sendVoiceToTextResult}
                            >
                                发送文字
                            </button>
                        </div>
                    </div>
                </div>
            )}

            {selectedFiles.length > 0 && (
                <div style={{ display: 'flex', gap: 8, padding: 10, background: '#fff', borderTop: '1px solid #eee', position: 'fixed', bottom: 0, left: 0, right: 0, zIndex: 1000 }}>
                    {selectedFiles.map((file, idx) => {
                        const url = URL.createObjectURL(file);
                        if (file.type.startsWith('image/')) {
                            return <img key={idx} src={url} alt="preview" style={{ width: 60, height: 60, objectFit: 'cover', borderRadius: 6 }} />;
                        } else if (file.type.startsWith('video/')) {
                            return <video key={idx} src={url} controls style={{ width: 60, height: 60, objectFit: 'cover', borderRadius: 6 }} />;
                        }
                        return null;
                    })}
                    <button style={{ marginLeft: 12 }} onClick={async () => {
                        for (const file of selectedFiles) {
                            const url = URL.createObjectURL(file);
                            let msg = null;
                            if (file.type.startsWith('image/')) {
                                msg = {
                                    id: Date.now() + Math.random(),
                                    type: 'image',
                                    sender: userId,
                                    isOwn: true,
                                    fileName: file.name,
                                    fileUrl: url,
                                    timestamp: new Date().toISOString()
                                };
                            } else if (file.type.startsWith('video/')) {
                                msg = {
                                    id: Date.now() + Math.random(),
                                    type: 'video',
                                    sender: userId,
                                    isOwn: true,
                                    fileName: file.name,
                                    fileUrl: url,
                                    timestamp: new Date().toISOString()
                                };
                            }
                            if (msg) setMessages(prev => [...prev, msg]);
                        }
                        setSelectedFiles([]);
                    }}>发送</button>
                    <button style={{ marginLeft: 6 }} onClick={() => setSelectedFiles([])}>取消</button>
                </div>
            )}
            {showImagePicker && (
              <div
                style={{
                  position: 'fixed', left: 0, right: 0, top: 0, bottom: 0,
                  background: '#000', zIndex: 2000,
                  display: 'flex', flexDirection: 'column', justifyContent: 'flex-end', padding: 0, margin: 0
                }}
                onClick={closeImagePicker}
              >
                <div
                  style={{
                    width: '100vw',
                    height: '100vh',
                    background: '#000',
                    borderTopLeftRadius: 16,
                    borderTopRightRadius: 16,
                    boxShadow: '0 -2px 16px rgba(0,0,0,0.08)',
                    transform: imagePickerVisible ? 'translateY(0)' : 'translateY(100%)',
                    transition: 'transform 0.3s cubic-bezier(.34,1.56,.64,1)',
                    overflow: 'hidden',
                    position: 'relative',
                    display: 'flex', flexDirection: 'column', justifyContent: 'space-between',
                    padding: 0, margin: 0
                  }}
                  onClick={e => e.stopPropagation()}
                >
                  {/* 顶部微信风格头部 */}
                  <div style={{
                    height: 48,
                    background: '#333',
                    display: 'flex',
                    alignItems: 'center',
                    justifyContent: 'center',
                    position: 'relative',
                  }}>
                    {/* 关闭按钮 */}
                    <span
                      style={{
                        position: 'absolute',
                        left: 16,
                        top: 0,
                        height: '100%',
                        display: 'flex',
                        alignItems: 'center',
                        color: '#fff',
                        fontSize: 28,
                        cursor: 'pointer',
                        fontWeight: 'bold',
                        zIndex: 2,
                      }}
                      onClick={closeImagePicker}
                    >
                      ×
                    </span>
                    {/* 中间标签 */}
                    <span style={{
                      background: 'rgba(0,0,0,0.25)',
                      color: '#fff',
                      borderRadius: 20,
                      padding: '4px 18px 4px 14px',
                      fontSize: 18,
                      fontWeight: 500,
                      display: 'flex',
                      alignItems: 'center',
                    }}>
                      图片和视频
                      <svg style={{marginLeft: 4}} width="22" height="22" viewBox="0 0 22 22"><circle cx="11" cy="11" r="11" fill="#888"/><path d="M7 9l4 4 4-4" stroke="#fff" strokeWidth="2" fill="none"/></svg>
                    </span>
                  </div>
                  {/* 图片网格 */}
                  <div style={{ flex: 1, overflowY: 'auto', padding: 0, margin: 0 }}>
                    <div style={{
                      display: 'grid',
                      gridTemplateColumns: 'repeat(4, 1fr)',
                      gap: '0px',
                      width: '100%',
                      height: '100%',
                      margin: 0,
                      padding: 0
                    }}>
                      {publicImages.map((img, idx) => {
                        const selectedIdx = selectedPublicImages.indexOf(img);
                        return (
                          <div
                            key={idx}
                            style={{
                              position: 'relative',
                              width: '100%',
                              aspectRatio: '1/1',
                              background: '#eee',
                              border: selectedIdx !== -1 ? '3px solid #06c05f' : '2px solid #000',
                              boxSizing: 'border-box',
                              cursor: 'pointer',
                              overflow: 'hidden',
                              margin: 0,
                              padding: 0
                            }}
                            onClick={() => {
                              setSelectedPublicImages(prev =>
                                prev.includes(img)
                                  ? prev.filter(i => i !== img)
                                  : [...prev, img]
                              );
                            }}
                          >
                            <img
                              src={img}
                              alt=""
                              style={{
                                width: '100%',
                                height: '100%',
                                objectFit: 'cover',
                                display: 'block',
                                margin: 0,
                                padding: 0
                              }}
                            />
                            {selectedIdx !== -1 && (
                              <div style={{
                                position: 'absolute',
                                top: 6,
                                right: 6,
                                width: 22,
                                height: 22,
                                background: '#06c05f',
                                color: '#fff',
                                borderRadius: '50%',
                                display: 'flex',
                                alignItems: 'center',
                                justifyContent: 'center',
                                fontSize: 14,
                                fontWeight: 'bold',
                                zIndex: 2,
                                boxShadow: '0 1px 4px #aaa',
                              }}>{selectedIdx + 1}</div>
                            )}
                          </div>
                        );
                      })}
                    </div>
                  </div>
                  {/* 微信风格底部操作栏 */}
                  <div style={{
                    background: '#333',
                    color: '#fff',
                    display: 'flex',
                    alignItems: 'center',
                    justifyContent: 'space-between',
                    height: 48,
                    padding: '0 18px',
                    fontSize: 18,
                    borderTop: '1px solid #222',
                  }}>
                    <span style={{color: '#fff', opacity: 0.95, fontSize: 16, fontWeight: 500}}>预览</span>
                    <span style={{display: 'flex', alignItems: 'center', color: '#fff', fontSize: 16}}>
                      <span style={{
                        width: 22, height: 22, border: '2px solid #fff', borderRadius: '50%', display: 'inline-flex', alignItems: 'center', justifyContent: 'center', marginRight: 6
                      }}>
                        {false && <span style={{width: 12, height: 12, background: '#06c05f', borderRadius: '50%', display: 'block'}}></span>}
                      </span>
                      原图
                    </span>
                    <button
                      style={{
                        background: selectedPublicImages.length === 0 ? '#444' : '#06c05f',
                        color: '#fff',
                        border: 'none',
                        borderRadius: 8,
                        padding: '6px 22px',
                        fontSize: 18,
                        fontWeight: 'bold',
                        cursor: selectedPublicImages.length === 0 ? 'not-allowed' : 'pointer',
                        opacity: selectedPublicImages.length === 0 ? 0.5 : 1,
                        transition: 'background 0.2s',
                      }}
                      disabled={selectedPublicImages.length === 0}
                      onClick={() => {
                        selectedPublicImages.forEach(img => {
                          // 1. 发送图片消息到 WebSocket
                          if (ws && ws.readyState === 1) {
                            ws.send(JSON.stringify({
                              type: 'image',
                              fileUrl: img, // public 目录下的相对路径
                              senderId: userId,
                              receiverId: targetUserId,
                              timestamp: new Date().toISOString()
                            }));
                          }
                        });
                        // 2. 本地也显示图片消息
                        setMessages(prev => [
                          ...prev,
                          ...selectedPublicImages.map(img => ({
                            id: Date.now() + Math.random(),
                            type: 'image',
                            sender: userId,
                            isOwn: true,
                            fileUrl: img,
                            timestamp: new Date().toISOString()
                          }))
                        ]);
                        setShowPlusPanel(false);
                        closeImagePicker();
                        setSelectedPublicImages([]);
                        setTimeout(() => {
                          scrollToBottom();
                        }, 100);
                      }}
                    >
                      发送{selectedPublicImages.length > 0 ? `(${selectedPublicImages.length})` : ''}
                    </button>
                  </div>
                </div>
              </div>
            )}
            {previewImages.length > 0 && (
              <PreviewModal
                images={previewImages}
                index={previewIndex}
                setIndex={setPreviewIndex}
                onClose={() => setPreviewImages([])}
                tip={previewTip}
                setTip={setPreviewTip}
              />
            )}
            {showCallSheet && (
              <div className={style.actionSheetMask} onClick={() => setShowCallSheet(false)}>
                <div className={style.actionSheet} onClick={e => e.stopPropagation()}>
                  <div className={style.actionSheetItem} tabIndex={0}>
                    <span className={style.iconVideo} /> 视频通话
                  </div>
                  <div className={style.actionSheetItem} tabIndex={0}>
                    <span className={style.iconPhone} /> 语音通话
                  </div>
                  <div className={style.actionSheetCancel} tabIndex={0} onClick={() => setShowCallSheet(false)}>
                    取消
                  </div>
                </div>
              </div>
            )}
            {showCamera && (
                <CameraModal
                    onClose={() => setShowCamera(false)}
                    onCapture={(file, type) => {
                        if (type === 'video') {
                            setMessages(prev => [...prev, {
                                id: Date.now() + Math.random(),
                                type: 'video',
                                sender: userId,
                                isOwn: true,
                                fileUrl: file,
                                timestamp: new Date().toISOString()
                            }]);
                            
                            // 视频消息发送完成后自动滚动到底部
                            setTimeout(() => {
                                scrollToBottom();
                            }, 100);
                        } else {
                            setMessages(prev => [...prev, {
                                id: Date.now() + Math.random(),
                                type: 'image',
                                sender: userId,
                                isOwn: true,
                                fileUrl: file,
                                timestamp: new Date().toISOString()
                            }]);
                            
                            // 图片消息发送完成后自动滚动到底部
                            setTimeout(() => {
                                scrollToBottom();
                            }, 100);
                        }
                    }}
                />
            )}
            {/* 小火人悬浮在聊天区右上角，只在有消息时显示 */}
            {messages.length > 0 && (
              <div className={style.firemanFloatTopRight} onClick={() => setShowFiremanPicker(true)} style={{cursor:'pointer'}}>
                <FireMan type={firemanKey} />
              </div>
            )}
            {showChatHistory && (
              <div
                style={{
                  position: 'fixed',
                  top: 0,
                  left: 0,
                  width: '100vw',
                  height: '100vh',
                  background: 'rgba(0,0,0,0.96)',
                  zIndex: 3000,
                  display: 'flex',
                  flexDirection: 'column',
                  alignItems: 'center',
                  justifyContent: 'flex-start',
                  color: '#fff',
                  paddingTop: 40
                }}
                onClick={() => setShowChatHistory(false)}
              >
                <div style={{fontSize: 28, marginBottom: 24, fontWeight: 600}}>聊天频率</div>
                {/* 数据卡片区 */}
                <div style={{ display: 'flex', gap: 48, marginBottom: 32 }}>
                  <div>
                    <div style={{ fontSize: 18, color: '#aaa' }}>总消息数</div>
                    <div style={{ fontSize: 32, fontWeight: 700, margin: '4px 0' }}>{(totalCount/10000>=1 ? (totalCount/10000).toFixed(2)+'万': totalCount)}</div>
                  </div>
                  <div>
                    <div style={{ fontSize: 18, color: '#aaa' }}>30日活跃天数</div>
                    <div style={{ fontSize: 32, fontWeight: 700, margin: '4px 0' }}>{activeDays}</div>
                  </div>
                  <div>
                    <div style={{ fontSize: 18, color: '#aaa' }}>近7日变化</div>
                    <div style={{ fontSize: 32, fontWeight: 700, margin: '4px 0', color: percent7 >= 0 ? '#00e676' : '#ff5252' }}>{percent7}%</div>
                  </div>
                </div>
                {/* 折线图区 */}
                <div style={{ width: '90vw', maxWidth: 600, height: 260, background: '#181818', borderRadius: 16, padding: 16, boxSizing: 'border-box', boxShadow: '0 2px 12px #0008', marginBottom: 24 }} onClick={e => e.stopPropagation()}>
                  <ResponsiveContainer width="100%" height="100%">
                    <LineChart data={freqData} margin={{ top: 24, right: 24, left: 8, bottom: 8 }}>
                      <CartesianGrid stroke="#222" vertical={false} />
                      <XAxis dataKey="date" stroke="#aaa" fontSize={12} tickLine={false} axisLine={false} />
                      <YAxis stroke="#aaa" fontSize={12} tickLine={false} axisLine={false} width={32} />
                      <Tooltip contentStyle={{ background: '#222', border: 'none', color: '#fff', fontSize: 14 }} labelStyle={{ color: '#fff' }} />
                      <Line type="monotone" dataKey="count" stroke="#4fc3f7" strokeWidth={3} dot={{ r: 3, fill: '#4fc3f7' }} activeDot={{ r: 6, fill: '#fff' }} />
                    </LineChart>
                  </ResponsiveContainer>
                </div>
                <div style={{fontSize: 16, opacity: 0.7}}>（点击任意处关闭）</div>
              </div>
            )}
            {/* 小火人选择弹窗 */}
            {showFiremanPicker && (
              <div className={style.firemanPickerMaskCute} onClick={() => setShowFiremanPicker(false)}>
                <div className={style.firemanPickerCute} onClick={e => e.stopPropagation()}>
                  <div className={style.firemanPickerHeaderCute}>
                    <span className={style.firemanPickerTitleCute}>选择你的可爱火人</span>
                    <button className={style.firemanPickerCloseCute} onClick={() => setShowFiremanPicker(false)}>×</button>
                  </div>
                  {/* 大号火人展示区 */}
                  <div style={{ display: 'flex', justifyContent: 'center', margin: '24px 0 12px 0' }}>
                    <FireMan type={pendingFiremanKey} size={120} />
                  </div>
                  <div className={style.firemanListCute}>
                    {FIREMAN_LIST.map(f => (
                      <div
                        key={f.key}
                        className={
                          style.firemanItemCute +
                          (pendingFiremanKey === f.key ? ' ' + style.firemanItemSelectedCute : '')
                        }
                        tabIndex={0}
                        onClick={() => setPendingFiremanKey(f.key)}
                        onKeyDown={e => {
                          if (e.key === 'Enter' || e.key === ' ') {
                            setPendingFiremanKey(f.key);
                          }
                        }}
                      >
                        <div className={style.firemanAvatarWrapCute}>
                          <FireMan type={f.key} />
                          {pendingFiremanKey === f.key && <span className={style.firemanCheckMarkCute}>✔</span>}
                        </div>
                        <div className={style.firemanNameCute}>{f.name}</div>
                        <div className={style.firemanDescCute}>{f.desc}</div>
                      </div>
                    ))}
                  </div>
                  {/* 确定按钮 */}
                  <button
                    style={{
                      width: '100%',
                      background: '#ff6fa1',
                      color: '#fff',
                      border: 'none',
                      borderRadius: 12,
                      fontSize: 20,
                      fontWeight: 600,
                      padding: '14px 0',
                      marginTop: 24,
                      cursor: 'pointer',
                      boxShadow: '0 2px 8px #ffb6d5',
                      letterSpacing: 2
                    }}
                    onClick={() => {
                      setFiremanKey(pendingFiremanKey);
                      setShowFiremanPicker(false);
                    }}
                  >
                    确定
                  </button>
                </div>
              </div>
            )}
            {showLocationModal && (
              <div style={{position:'fixed',left:0,top:0,right:0,bottom:0,zIndex:9999,background:'#f5f5f5',display:'flex',flexDirection:'column'}}>
                {/* 顶部栏 */}
                <div style={{background:'#444',height:110,position:'relative',display:'flex',flexDirection:'column',alignItems:'center',justifyContent:'center'}}>
                  <button style={{position:'absolute',left:18,top:18,fontSize:28,background:'none',border:'none',color:'#fff',cursor:'pointer'}} onClick={()=>setShowLocationModal(false)}>×</button>
                  <div style={{display:'flex',gap:18,marginTop:18}}>
                    <img src="/个人头像.png" style={{width:54,height:54,borderRadius:'50%',border:'2px solid #fff'}} alt="avatar1" />
                    <img src="/微信头像.jpg" style={{width:54,height:54,borderRadius:'50%',border:'2px solid #fff'}} alt="avatar2" />
                  </div>
                  <div style={{color:'#fff',fontSize:22,fontWeight:700,marginTop:8}}>2人在共享位置</div>
                </div>
                {/* 真实地图区域 */}
                <div style={{flex:1,position:'relative',background:'#eaeaea'}}>
                  <div id="tmapContainer" style={{width:'100%',height:'100%'}}></div>
                  {/* 底部圆形按钮 */}
                  <div style={{position:'absolute',left:'50%',bottom:40,transform:'translateX(-50%)',width:64,height:64,borderRadius:'50%',background:'#444',display:'flex',alignItems:'center',justifyContent:'center',boxShadow:'0 2px 8px #0004'}}>
                    <div style={{width:32,height:32,borderRadius:'50%',background:'#888',display:'flex',alignItems:'center',justifyContent:'center',color:'#fff',fontSize:22}}>●</div>
                  </div>
                </div>
              </div>
            )}
        </div>
    );
}

function PreviewModal({ images, index, setIndex, onClose, tip, setTip }) {
  // 键盘切换
  React.useEffect(() => {
    if (images.length > 0) {
      const handleKeyDown = (e) => {
        if (e.key === 'ArrowLeft') {
          if (index === 0) {
            setTip('已经是第一张');
            if (window.__previewTipTimer) clearTimeout(window.__previewTipTimer);
            window.__previewTipTimer = setTimeout(() => setTip(''), 1000);
          } else {
            setIndex(i => i - 1);
          }
        } else if (e.key === 'ArrowRight') {
          if (index === images.length - 1) {
            setTip('已经是最后一张');
            if (window.__previewTipTimer) clearTimeout(window.__previewTipTimer);
            window.__previewTipTimer = setTimeout(() => setTip(''), 1000);
          } else {
            setIndex(i => i + 1);
          }
        } else if (e.key === 'Escape') {
          onClose();
        }
      };
      window.addEventListener('keydown', handleKeyDown);
      return () => window.removeEventListener('keydown', handleKeyDown);
    }
  }, [images, index, setIndex, setTip, onClose]);
  // 清理定时器
  React.useEffect(() => () => { if (window.__previewTipTimer) clearTimeout(window.__previewTipTimer); }, []);

  return (
    <div
      style={{
        position: 'fixed', left: 0, top: 0, right: 0, bottom: 0, zIndex: 3000,
        background: 'rgba(0,0,0,0.85)', display: 'flex', alignItems: 'center', justifyContent: 'center'
      }}
      onClick={onClose}
    >
      <div style={{ position: 'relative', display: 'flex', alignItems: 'center' }} onClick={e => e.stopPropagation()}>
        {/* 上一张 - 隐藏但保留功能 */}
        <button
          style={{
            position: 'absolute', left: 16, top: '50%', transform: 'translateY(-50%)',
            width: 60, height: 60, borderRadius: '50%', background: 'rgba(0,0,0,0.65)',
            border: 'none', display: 'none', // 隐藏按钮
            alignItems: 'center', justifyContent: 'center',
            color: '#fff', fontSize: 38, cursor: 'pointer', zIndex: 2, boxShadow: '0 4px 16px #0006',
            transition: 'background 0.2s, box-shadow 0.2s',
          }}
          onMouseOver={e => e.currentTarget.style.background = 'rgba(0,0,0,0.85)'}
          onMouseOut={e => e.currentTarget.style.background = 'rgba(0,0,0,0.65)'}
          onClick={e => {
            e.stopPropagation();
            if (index === 0) {
              setTip('已经是第一张');
              if (window.__previewTipTimer) clearTimeout(window.__previewTipTimer);
              window.__previewTipTimer = setTimeout(() => setTip(''), 1000);
            } else {
              setIndex(i => i - 1);
            }
          }}
          aria-label="上一张"
        >
          <svg width="36" height="36" viewBox="0 0 36 36"><circle cx="18" cy="18" r="18" fill="none"/><polyline points="23,9 13,18 23,27" fill="none" stroke="#fff" strokeWidth="4" strokeLinecap="round" strokeLinejoin="round"/></svg>
        </button>
        {/* 媒体内容 */}
        {images[index]?.type === 'image' ? (
          <img src={images[index].url} alt="" style={{ maxWidth: '90vw', maxHeight: '80vh', borderRadius: 10 }} />
        ) : images[index]?.type === 'video' ? (
          <video src={images[index].url} controls style={{ maxWidth: '90vw', maxHeight: '80vh', borderRadius: 10, background: '#000' }} />
        ) : null}
        {/* 下一张 - 隐藏但保留功能 */}
        <button
          style={{
            position: 'absolute', right: 16, top: '50%', transform: 'translateY(-50%)',
            width: 60, height: 60, borderRadius: '50%', background: 'rgba(0,0,0,0.65)',
            border: 'none', display: 'none', // 隐藏按钮
            alignItems: 'center', justifyContent: 'center',
            color: '#fff', fontSize: 38, cursor: 'pointer', zIndex: 2, boxShadow: '0 4px 16px #0006',
            transition: 'background 0.2s, box-shadow 0.2s',
          }}
          onMouseOver={e => e.currentTarget.style.background = 'rgba(0,0,0,0.85)'}
          onMouseOut={e => e.currentTarget.style.background = 'rgba(0,0,0,0.65)'}
          onClick={e => {
            e.stopPropagation();
            if (index === images.length - 1) {
              setTip('已经是最后一张');
              if (window.__previewTipTimer) clearTimeout(window.__previewTipTimer);
              window.__previewTipTimer = setTimeout(() => setTip(''), 1000);
            } else {
              setIndex(i => i + 1);
            }
          }}
          aria-label="下一张"
        >
          <svg width="36" height="36" viewBox="0 0 36 36"><circle cx="18" cy="18" r="18" fill="none"/><polyline points="13,9 23,18 13,27" fill="none" stroke="#fff" strokeWidth="4" strokeLinecap="round" strokeLinejoin="round"/></svg>
        </button>
        {/* 关闭按钮 */}
        <button
          style={{ position: 'absolute', top: -50, right: 0, fontSize: 22, color: '#fff', background: 'rgba(0,0,0,0.4)', border: 'none', borderRadius: 8, padding: '6px 18px', cursor: 'pointer' }}
          onClick={e => { e.stopPropagation(); onClose(); }}
        >关闭</button>
        {/* 提示 */}
        {tip && (
          <div style={{
            position: 'fixed', left: '50%', bottom: 60, transform: 'translateX(-50%)',
            background: 'rgba(0,0,0,0.8)', color: '#fff', borderRadius: 8, padding: '10px 28px',
            fontSize: 18, fontWeight: 500, zIndex: 9999, pointerEvents: 'none',
          }}>{tip}</div>
        )}
      </div>
    </div>
  );
}